﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using UtilityLibrary;
using Enemies;
using MapLibrary.Sound;
using MapLibrary.Particles.Emitters;
using Animation;
using AI;
using MapLibrary.Enemies;

namespace MapLibrary.Players.Skills
{
    class Explosion : Skill
    {
        private const int EXPLOSION_POWER = 10000;

        /// <summary>
        /// Radius of the explosion IN METERS!
        /// </summary>
        private LevelDepValue<float> explosionRadius;

        /// <summary>
        /// Cooldown of the explosion in frames.
        /// </summary>
        private LevelDepValue<int> coolDown;

        /// <summary>
        /// The damage the explosion does.
        /// </summary>
        private LevelDepValue<int> damage;

        private Player player;
        private ExplosionEmitter explosionEmitter;

        /// <summary>
        /// The number of frames since the last activation
        /// of the explosion
        /// </summary>
        private int coolDownCount;

        private bool activatable;


        public Explosion() 
            : base()
        {
            SetLevelThresholds();

            coolDownCount = 0;
            activatable = true;
        }

        public override void Update()
        {
            if(!activatable)
                UpdateCoolDownCount();
        }

        public override void Activate(Player p)
        {
            if (player == null)
                InitParticleEffect(p);

            if (activatable)
            {
                explosionEmitter.Trigger();
                PushNearbySprites(p);
                activatable = false;
                SoundManager.GetSoundManager().PlayCue("nanobot_explosion");
            }
        }

        private void InitParticleEffect(Player p)
        {
            player = p;
            explosionEmitter = new ExplosionEmitter(p.World.ParticleManager, p);
        }

        public override void DrawAtPlayer(SpriteBatch batch, WorldViewport viewport, Player p)
        {

            Vector2 position = viewport.WorldToViewportCoords(p.Position);
            position = position - p.GetCenter() - new Vector2(0, 40);

            if (!activatable)
            {
                PrimitiveBrush.RenderText(batch, "Explosion Cool Down: " + (coolDown.Value - coolDownCount) / 60, position);
            }
            else
                PrimitiveBrush.RenderText(batch, "Explosion Available", position);
        }

        public override bool ActivateConditionsSatisfied(Player p)
        {
            return activatable;
        }

        private void PushNearbySprites(Player p)
        {
            World world = p.World.PhysicsWorld;

            //inefficient implementation, not sure if it will scale.
            List<Body> bodiesInRadius = new List<Body>();
            foreach (Body b in world.BodyList)
            {
                if (b == p.Body)
                    continue;

                if ((b.UserData as TuberculosisSpit) != null)
                    continue;

                if ((b.UserData as LevelCollisionRegion) != null)
                    continue;

                float d = Vector2.Distance(
                    b.Position, p.Body.Position);

                if (d <= explosionRadius.Value)
                    bodiesInRadius.Add(b);
            }

            foreach (Body b in bodiesInRadius)
            {
                Vector2 diff = b.Position - p.Body.Position;
                float d = Vector2.Distance(
                    b.Position, p.Body.Position);
                float power = EXPLOSION_POWER;
                Vector2 diffNorm = Vector2.Normalize(diff);
                Vector2 force = diffNorm * power;
                b.ApplyForce(force);

                if ((b.UserData as ExplodeableObject) != null)
                {
                    ExplodeableObject obj = b.UserData as ExplodeableObject;
                    obj.Explode();
                }

                if ((b.UserData as Malaria) != null)
                {
                    MalariaBot malBot = (b.UserData as Malaria).Bot as MalariaBot;
                    malBot.Stun();
                }
            }
        }

        private void UpdateCoolDownCount()
        {
            coolDownCount++;

            if (coolDownCount > coolDown.Value)
            {
                coolDownCount = 0;
                activatable = true;
            }
        }

        private void SetLevelThresholds()
        {
            explosionRadius = new LevelDepValue<float>(this);
            explosionRadius.AddValueForLevel(1, 2.0f);
            explosionRadius.AddValueForLevel(2, 3.0f);

            coolDown = new LevelDepValue<int>(this);
            coolDown.AddValueForLevel(1, 600);
            coolDown.AddValueForLevel(3, 420);

            damage = new LevelDepValue<int>(this);
            damage.AddValueForLevel(1, 10);
            damage.AddValueForLevel(4, 25);
        }

        public override int GetAmount(string trackerName)
        {
            if (activatable && coolDownCount == 0)
            {
                return coolDown.Value;
            }
            else
                return coolDownCount;
        }

        public override int GetMax(string trackerName)
        {
            return coolDown.Value;
        }
    }
}
